package com.william.jdbcplus.core.toolkit.lambda;

import com.william.jdbcplus.core.toolkit.ClassUtils;
import com.william.jdbcplus.core.toolkit.ExceptionUtils;

import java.lang.invoke.SerializedLambda;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SerializedLambdaUtil {
    /**
     * 获取接口 class
     *
     * @return 返回 class 名称
     */
    public static String getFunctionalInterfaceClassName(SerializedLambda lambda) {
        return normalName(lambda.getFunctionalInterfaceClass());
    }

    /**
     * 获取实现的 class
     *
     * @return 实现类
     */
    public static Class<?> getImplClass(SerializedLambda lambda) {
        return ClassUtils.toClassConfident(getImplClassName(lambda));
    }

    /**
     * 获取 class 的名称
     *
     * @return 类名
     */
    public static String getImplClassName(SerializedLambda lambda) {
        return normalName(lambda.getImplClass());
    }

    /**
     * 获取实现者的方法名称
     *
     * @return 方法名称
     */
    public static String getImplMethodName(SerializedLambda lambda) {
        return lambda.getImplMethodName();
    }

    /**
     * 正常化类名称，将类名称中的 / 替换为 .
     *
     * @param name 名称
     * @return 正常的类名
     */
    private static String normalName(String name) {
        return name.replace('/', '.');
    }

    private static final Pattern INSTANTIATED_METHOD_TYPE = Pattern.compile("\\(L(?<instantiatedMethodType>[\\S&&[^;)]]+);\\)L[\\S]+;");

    public static Class<?> getInstantiatedMethodType(SerializedLambda lambda) {
        Matcher matcher = INSTANTIATED_METHOD_TYPE.matcher(lambda.getInstantiatedMethodType());
        if (matcher.find()) {
            return ClassUtils.toClassConfident(normalName(matcher.group("instantiatedMethodType")));
        }
        throw ExceptionUtils.mpe("无法从 %s 解析调用实例。。。", lambda.getInstantiatedMethodType());
    }

    /**
     * @return 字符串形式
     */
    public static String toString(SerializedLambda lambda) {
        return String.format("%s -> %s::%s", getFunctionalInterfaceClassName(lambda), getImplClass(lambda).getSimpleName(), lambda.getImplMethodName());
    }

}
